#include "Token.h"
#include <QtCore/QStringList>
#include <QtCore/QDataStream>

Trace::Trace()
{
}

Trace::Trace(int ordinal, int amount)
{
	mItems.insert(ordinal);
	mAmount = amount;
}

Trace::Trace(const Trace& other)
  : mItems(other.contains()), mAmount(other.mount())
{
}

bool Trace::parseContent(const QDomElement& element)
{
	//QStringList items = element.attribute("item").split(',');
	//foreach(QString one, items)
	//	mItems.insert(one.toInt());
	stringToContains(element.attribute("item"));
	mAmount = element.attribute("amount").toInt();
	return true;
}

void Trace::writeContent(QXmlStreamWriter& writer) const
{
	writer.writeStartElement("trace");
	writer.writeAttribute("item", containsToString());
	writer.writeAttribute("amount", QString("%1").arg(mAmount));
	writer.writeEndElement();
}

void Trace::stringToContains(const QString & str)
{
	mItems.clear();
	QStringList items = str.split(',');
	foreach(QString one, items)
		mItems.insert(one.toInt());
}

QString Trace::containsToString() const
{
	QStringList list;
	foreach(int i, mItems)
		list << QString("%1").arg(i);
	return list.join(",");
}

Trace& Trace::combine(const Trace& other)
{
	Q_ASSERT_X(mAmount == other.mount(), "Token.cpp", "Traces combined should have the same mount");

	mItems.unite(other.contains());
	return *this;
}

bool Trace::isPartial() const
{
	return (mItems.size() != 1); 
}

QSet<int> Trace::contains() const { return mItems; }

int Trace::mount() const { return mAmount; }


bool Trace::isComplete() const { return (mItems.size() == mAmount); }

void Trace::addItem(int i)
{
	if (mItems.contains(i)) 
		qWarning("the item %1 have already existed in the Trace", i);

	mItems.insert(i);
}

Token::Token(const QVariant& var)
  : QVariant(var)
{
}

Token::~Token(void)
{
}

bool Token::parseContent(const QDomElement& element)
{
	QString	valueString = element.attribute("value");
	QByteArray bytes = QByteArray::fromBase64(valueString.toAscii());
	QDataStream stream(&bytes, QIODevice::ReadOnly);

	//QVariant var( nameToType(element.attribute("type").toAscii()) );
	//QVariant var;
	stream >> *this;
	//*this = var;
	
	Trace trace;
	QDomElement sub = element.firstChildElement();
	while (!sub.isNull()) {
		trace.parseContent(sub);
		mTraceStack.push(trace);

		sub = sub.nextSiblingElement();
	}
	return true;
}

void Token::writeContent(QXmlStreamWriter& writer) const
{
	writer.writeStartElement("token");
	writer.writeAttribute("type", typeName());

	QByteArray bytes;
	QDataStream stream(&bytes, QIODevice::WriteOnly);
	stream << *this;
	writer.writeAttribute("value", bytes.toBase64());
	
	foreach (Trace trace, mTraceStack) 
		trace.writeContent(writer);

	writer.writeEndElement();
}

void Token::setVariant(const QVariant & var) //how to call father operator =
{
	//static_cast<QVariant>(*this) = var; //no use. temp QVariant object
	QVariant::operator =(var);
}

//split this token to a list of tokens
void Token::split(QList<Token*>& tokens)
{
	Q_ASSERT_X(canConvert(QVariant::List), 
		"Token.cpp", "make sure this token is a collection token");

	QList<QVariant> valueList = toList();

	//3 cases: only one item(to be splitted deeper), partial, no trace
	bool partial = false;
	QSet<int> set;
	QSet<int>::iterator it;
	quint32 mount = valueList.size();

	if ( !mTraceStack.empty() ) {
		Trace trace = mTraceStack.top();
		if ( trace.isPartial() ) {
			//partial
			mTraceStack.pop();
			
			mount = trace.mount();
			set = trace.contains();
			it = set.begin();
			partial = true;
		}
	}

	quint32 i = 0;
	foreach (QVariant var, valueList)
	{
		Token* token = new Token(var);
		
		token->setTraceStack(mTraceStack);
		if (partial) 
			token->pushTrace( Trace(*it++, mount));
		else 
			token->pushTrace( Trace(++i, mount));
		tokens.append(token);
	}
}

//Token& Token::combine(const QList<Token*>& tokens) //should support part combine 
//{
//
//}

void Token::setTraceStack(const QStack<Trace>& traceStack)
{
	mTraceStack = traceStack;
}

const QStack<Trace>& Token::trace() const
{
	return mTraceStack;
}

void Token::pushTrace(const Trace& trace)
{
	mTraceStack.push(trace);
}

void Token::popTrace()
{
	mTraceStack.pop();
}

Trace& Token::topTrace()
{
	return mTraceStack.top();
}